package com.star.cat.persitent.aspect;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.star.cat.common.parse.DefaultKeyParse;
import com.star.cat.common.parse.KeyParse;
import com.star.cat.common.parse.ResultParse;
import com.star.cat.persitent.annotation.Persistent;
import com.star.cat.persitent.model.PerModel;
import com.star.cat.persitent.service.PerService;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class PersistentAspect {
	
	@Autowired
	private KeyParse keyParse;
	@Autowired
	private PerService perService;

	private ConcurrentHashMap<String, KeyParse> keyParseMap = new ConcurrentHashMap<>();
	private ConcurrentHashMap<String, ResultParse> resultParseMap = new ConcurrentHashMap<>();
	
	/**
	 * 切点
	 */
	@Pointcut("@annotation(com.star.cat.persitent.annotation.Persistent)")
	public void pointCut() {
	}
	
	@Around("pointCut()&&@annotation(anno)")
	public Object around(ProceedingJoinPoint invocation, Persistent anno) throws Throwable{
		MethodSignature signature = (MethodSignature) invocation.getSignature();
        Method method = signature.getMethod();
        Class<?>[] paramsTypes = method.getParameterTypes();
        Object[] params = invocation.getArgs();
        Object result=null;
        String key="";
        try {
        	String value="";
			key = getKey(anno, paramsTypes, params);
			value = perService.getByKey(key);
			if (StringUtils.isNotBlank(value)) {
				Type returnType = method.getGenericReturnType();
				result = getResult(anno, value, returnType);
			}
		} catch (Exception e) {
			log.error("获取缓存失败：{}，",key,e);
		} finally {
			if (result == null) {
				result = invocation.proceed();
				if (StringUtils.isNotBlank(key)) {
					perService.save(new PerModel(key, result));
				}
			}
		}
		return result;
	}
	
	/**
	 * 解析Key
	 * @param anno
	 * @param params
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	private String getKey(Persistent anno, Class<?>[] paramsTypes, Object[] params)
			throws InstantiationException, IllegalAccessException {
		String keyParseName = anno.keyParse().getName();
		KeyParse keyParse = null;
		if (anno.keyParse().equals(DefaultKeyParse.class)) {
			keyParse = this.keyParse;
		} else {
			if (keyParseMap.containsKey(keyParseName)) {
				keyParse = keyParseMap.get(keyParseName);
			} else {
				keyParse = anno.keyParse().newInstance();
				keyParseMap.put(keyParseName, keyParse);
			}
		}
		return keyParse.getKey(anno.key(), paramsTypes, params);
	}
	
	/**
	 * 返回结果解析
	 * @param value
	 * @param returnType
	 * @return
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	private Object getResult(Persistent anno,String value,Type returnType) throws InstantiationException, IllegalAccessException{
		String parseClassName = anno.resultParse().getName();
		ResultParse parse = null;
		if (resultParseMap.containsKey(parseClassName)) {
			parse = resultParseMap.get(parseClassName);
		}else{
			parse = anno.resultParse().newInstance();
			resultParseMap.put(parseClassName, parse);
		}
		if (parse!=null&&StringUtils.isNotBlank(value)) {
			if (anno.result()[0].equals(Object.class)) {
				return parse.parse(value, returnType);
			}else{
				return parse.parse(value, returnType, anno.result());
			}
		}
		return null;
	}

}
